﻿using System;
using System.Runtime.InteropServices;
using System.Threading;
using System.Windows.Forms;
using QAToolsAddin.GenericDialogs;
using Excel = Microsoft.Office.Interop.Excel;

namespace QAToolsAddin.Components {
    public class ExcelForm : Form {
        [DllImport("user32.dll")]
        public static extern int SetForegroundWindow(IntPtr hWnd);

        private readonly Excel.Application _excelapp;
        private readonly Excel.Workbook _initialWorkbook;
        private readonly Excel.Worksheet _initialWorksheet;
        private readonly object _initialSelection;

        public bool RestoreSelectionOnExit { get; set; }

        private ExcelForm() {

        }

        public ExcelForm(Excel.Application excelapp) {
            Application.EnableVisualStyles();
            SuspendLayout();
            FormBorderStyle = FormBorderStyle.FixedDialog;
            MaximizeBox = false;
            MinimizeBox = false;
            ShowIcon = false;
            ShowInTaskbar = false;
            StartPosition = FormStartPosition.CenterScreen;
            base.FormClosed += ExcelForm_FormClosed;
            Application.ThreadException += Application_ThreadException;
            RestoreSelectionOnExit = true;
            if (excelapp == null) return;
            _excelapp = excelapp;
            _initialWorkbook = excelapp.ActiveWorkbook;
            _initialWorksheet = (Excel.Worksheet)excelapp.ActiveSheet;
            _initialSelection = excelapp.Selection;
            ResumeLayout();
        }

        internal virtual void Application_ThreadException(object sender, ThreadExceptionEventArgs e) {
            DialogResult = DialogResult.Abort;
            Hide();
            if (e.Exception is InputError)
                MsgBox.ShowError(_excelapp, e.Exception.Message);
            else
                new FrmException(_excelapp, e.Exception).ShowDialog();
            Close();
            GC.Collect();
            GC.WaitForPendingFinalizers();
        }

        public void RestoreSelection() {
            try {
                if ((Excel.Workbook)_excelapp.ActiveWorkbook != _initialWorkbook)
                    ((Excel._Workbook)_initialWorkbook).Activate();
                if ((Excel.Worksheet)_excelapp.ActiveSheet != _initialWorksheet)
                    ((Excel._Worksheet)_initialWorksheet).Activate();
                if (_excelapp.Selection != _initialSelection) {
                    if (_initialSelection is Excel.Range)
                        (_initialSelection as Excel.Range).Activate();
                    else
                        _initialSelection.GetType().InvokeMember("Select", System.Reflection.BindingFlags.InvokeMethod | System.Reflection.BindingFlags.Public, null, _initialSelection, null);
                }
            } catch { }
        }

        public new void Show() {
            base.Show(new Wrapper(_excelapp.Hwnd));
        }

        public new System.Windows.Forms.DialogResult ShowDialog() {
            return base.ShowDialog(new Wrapper(_excelapp.Hwnd));
        }

        public Excel.Application ExcelApp {
            get {
                return _excelapp;
            }
        }

        private void ExcelForm_FormClosed(object sender, System.Windows.Forms.FormClosedEventArgs e) {
            if (RestoreSelectionOnExit)
                RestoreSelection();
            SetForegroundWindow(new IntPtr(_excelapp.Hwnd));
        }

        private class Wrapper : System.Windows.Forms.IWin32Window {
            readonly IntPtr _handle;

            public Wrapper(int hwnd) {
                _handle = new IntPtr(hwnd);
            }

            public IntPtr Handle {
                get { return _handle; }
            }
        }

        private void InitializeComponent() {
            this.SuspendLayout();
            // 
            // ExcelForm
            // 
            this.ClientSize = new System.Drawing.Size(292, 266);
            this.Name = "ExcelForm";
            this.ResumeLayout(false);

        }

    }

    class InputError : Exception {
        internal InputError(string message)
            : base(message) {
        }
    }
}
